﻿using System;
using Google.Protobuf;
using GSE;
using GSE.Network;
using GSE.test.protos;
using NLog;
using GSE.Rpc;
using System.Threading;
using System.Threading.Tasks;
using CommandLine;
using CommandLine.Text;
using System.Text;
using GSE.protos;
using System.Diagnostics;

namespace client
{
    public class MLogger : NLog.Logger, GSE.ILogger
    {
    }


    class CommandLineArgs
    {

        [Option('h', "host", Required = false, Default = "127.0.0.1:3132", HelpText = "server address, e.g. 127.0.0.1:3132")]
        public string ServerAddr { get; set; }

        [Option('c', "concurrency", Required = false, Default = 1, HelpText = "number of concurrency tasks for sending requests.")]
        public int Concurrency { get; set; }

        [Option('v', "log-level", Required = false, Default = 0, HelpText = "log leve. 0 - debug, 1 - info, 2 - warn, 3 - error")]
        public GSE.LogLevel LogLevel { get; set; }

        [Option('t', "transport", Required = false, Default = "udp", HelpText = "transport protocol type, udp or tcp")]
        public String TransportProtocol { get; set; }

        [Option('n', "number-of-request", Required = false, Default = 1, HelpText = "number of requests to perform in each thread.")]
        public int NumberOfRequests { get; set; }

        [Option('p', "number-of-parallel-threads", Required = false, Default = 1, HelpText = "number of parrallel clients to simulate.")]
        public int NumberOfThreads { get; set; }

    }


    class Program
    {
        private static GSE.ILogger logger;

        static void Main(string[] args)
        {
            var opts = new CommandLineArgs();
            CommandLine.Parser.Default.ParseArguments<CommandLineArgs>(args).WithParsed<CommandLineArgs>(_opts => { opts = _opts; });
            if (opts.ServerAddr == null)
            {
                return;
            }
            var logLevel = (GSE.LogLevel)opts.LogLevel;
            logger = new GSELogger(logLevel);
            // 设置GSE logger实例.
            GSE.Logger.Init(logger);
            var ts = new Task[opts.NumberOfThreads];
            for (var i = opts.NumberOfThreads; i > 0; i--)
            {
                ts[i - 1] = Task.Run(() => run(opts));
            }

            for (var i = opts.NumberOfThreads; i > 0; i--)
            {
                ts[i - 1].Wait();
            }

        }

        protected static void run(CommandLineArgs opts)
        {
            TransportType? tt;
            if (opts.TransportProtocol == "tcp")
            {
                tt = TransportType.TCP;
            }
            else
            {
                tt = TransportType.UDP;
            }

            logger.Info($"starting client with {opts.TransportProtocol}, server address: {opts.ServerAddr}, concurrency: {opts.Concurrency}, {opts.NumberOfRequests} to perform on each routine.");
            var cfg = new ClientConfig();
            cfg.EventPushFrequency = 30;
            cfg.RemoteAdddr = opts.ServerAddr;
            cfg.TransportType = (TransportType)tt;
            var client = new Client(cfg);
            var tn = opts.Concurrency;

            client.Intialize(true);

            client.TransportReceiveBufferSize = 1024 * 1024;
            client.TransportSendBufferSize = 1024 * 1204;

            var cbfEvtCfg = new EventConfig();
            cbfEvtCfg.EventPushFrequency = 2;
            // 注册用于订阅client_battle_frames消息.
            client.GetEventPusher().RegisterSubscribableEvent("client_battle_frames", cbfEvtCfg);

            var heroStatus = new HeroStatusUpdateRequest();
            heroStatus.Health = 1000;
            heroStatus.HeroId = 1230;
            heroStatus.Name = "chaos";
            heroStatus.UserId = 2123;
            var rsp = new HeroStatusUpdateResponse();

            var wc = Stopwatch.StartNew();
            // 订阅推送
            var subP = new SubEvent();
            subP.Name = "sync_arena_frames";
            var subResult = client.Sub<BattleEvent>(subP, new ArenaFrameProcessor(logger));
            logger.Info($"subscribe event successfull. name: {subP.Name}, seriesID: {subResult.SeriesID}");

            var tasks = new Task[tn];
            for (var n = tn - 1; n >= 0; n--)
            {
                tasks[n] = Task.Run(() =>
                {
                    for (var i = 0; i < opts.NumberOfRequests; i++)
                    {
                        try
                        {
                            var btEvt = new BattleEvent();
                            btEvt.Command = "attack A";
                            btEvt.RoleId = 2001;
                            btEvt.SequenceNo = (uint)(100 + i);
                            // 向服务端推送消息/事件/帧
                            client.PubEvent("client_battle_frames", btEvt);
                            var result = client.Call("UpdateHeroStatus", heroStatus, rsp);
                            Console.WriteLine(result.ToString());
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.ToString());

                        }
                    }
                });
            }

            for (var n = tn - 1; n >= 0; n--)
            {
                tasks[n].Wait();
            }

            /* var tc = et.Subtract(st).Duration().TotalMilliseconds;
             Console.WriteLine($"start time: {st.ToLocalTime()}");
             Console.WriteLine($"end time: {et.ToLocalTime()}");
             */
            var tc = (float)wc.ElapsedMilliseconds;
            wc.Stop();
            Console.WriteLine($"time consumed: {tc} ms");
            var tq = (float)opts.NumberOfRequests * opts.Concurrency;
            Console.WriteLine($"total requests: {tq}");
            Console.WriteLine($"{tc / tq} ms per call");
            Console.WriteLine($"QPS: {tq / (tc / 1000)}");
            Console.ReadKey();
            client.Close();

        }
    }

    /// <summary>
    /// 处理服务端过来的 帧/消息/事件的推送
    /// </summary>
    public class ArenaFrameProcessor : IEventConsumer<BattleEvent>
    {
        private GSE.ILogger logger;
        public void Process(BattleEvent[] evts)
        {
            logger.Debug($"battle fames received...");
            logger.Debug($"received framed messages, count: {evts.Length}");
            for (var i = 0; i < evts.Length; i++)
            {
                var m = evts[i];
                logger.Debug($"MSG = RoleID: {m.RoleId}, Seq: {m.SequenceNo}, CMD: {m.Command}");
            }
        }
        public ArenaFrameProcessor(GSE.ILogger logger)
        {
            this.logger = logger;
        }
    }
}

